package com.filldream.fastboot.web.filter;

import com.filldream.fastboot.common.constant.FinalStr;
import com.filldream.fastboot.common.exception.GlobalException;
import com.filldream.fastboot.common.entity.*;
import com.filldream.fastboot.common.util.DDErrorUtil;
import com.filldream.fastboot.common.util.JsonUtil;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.exc.InvalidFormatException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.MDC;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.jdbc.BadSqlGrammarException;
import org.springframework.validation.FieldError;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;

/**
 * @author RickSun
 * @Description 异常拦截器
 * @data 2019-03-22 15:56
 **/
@Slf4j
@ControllerAdvice
@ResponseBody
public class ErrorHandler {

	//允许打印出sql错误的环境
	private static final String showSqlExceptionActive = "demo,family,local,test,dev";

	/**
	 * 自定义全局异常
	 * @param e
	 * @return
	 */
	@ExceptionHandler(GlobalException.class)
	public B handleGlobalException(GlobalException e){
		List<String> list = getErrMsg(e, "");
		ErrContent errContent = e.getErrContent();
		if( e.getSendDd() ){
			DDErrorUtil.directSend(list.get(1));
		}
		return B.create(errContent,errContent.getMsg(),list.get(0));
	}

	/**
	 * 捕获其他异常
	 * @param e
	 * @return
	 */
	@ExceptionHandler(Exception.class)
	public B handleExption(Exception e){
		List<String> list = getErrMsg(e, "");
		ErrContent serverBusy = ErrorCode.SERVER_BUSY.reMsg();
		DDErrorUtil.directSend(list.get(1));
		return B.create(serverBusy,serverBusy.getMsg(),list.get(0));
	}

	/**
	 * 捕获空针异常
	 * @param e
	 * @return
	 */
	@ExceptionHandler(NullPointerException.class)
	public B handleNullPointerException(Exception e){
		List<String> list = getErrMsg(e,"空针异常");
		DDErrorUtil.directSend(list.get(1));
		return B.create(ErrorCode.SERVER_BUSY.reMsg(),"系统繁忙，请稍后重试(NP)",list.get(0));
	}

	@ExceptionHandler(BadSqlGrammarException.class)
	public B handlerBusinessException(BadSqlGrammarException e) {
		log.error("查询异常:"+e.toString());
		return B.create(ErrorCode.DB_ERR.reMsg(),"系统繁忙，请稍后重试",showSqlExceptionActive.indexOf(FinalStr.getActive())>=0 ? e.toString() : "系统繁忙，请稍后重试(DB)");
	}

	@ExceptionHandler(HttpRequestMethodNotSupportedException.class)
	public B handlerHttpRequestMethodNotSupportedException(HttpRequestMethodNotSupportedException e) {
		String exMsg = "请勿使用[" + e.getMethod() + "]方法，推荐使用["+ e.getSupportedMethods()[0] +"]方法或参考接口文档";
		log.error(exMsg);
		return B.create(ErrorCode.HTTP_REQUEST_ERR.reMsg(),exMsg);
	}

	@ExceptionHandler(JsonMappingException.class)
	public B handlerException(JsonMappingException e) {
		log.error("JSON格式不正确_jmapping:"+ e.getMessage());
		return B.create(ErrorCode.PARAM_ERR.reMsg(),"JSON格式不正确_jmapping");
	}

	@ExceptionHandler(HttpMessageNotReadableException.class)
	public B handlerException(HttpMessageNotReadableException e) {
		log.error("JSON格式不正确_param:"+e.getMessage());
		return B.create(ErrorCode.PARAM_ERR_F.reMsg(),"JSON格式不正确_param");
	}

	@ExceptionHandler(JsonParseException.class)
	public B handlerException(JsonParseException e) {
		log.error("JSON格式不正确_json:"+ e.getMessage());
		return B.create(ErrorCode.PARAM_ERR.reMsg(),"JSON格式不正确_json");
	}

	@ExceptionHandler(InvalidFormatException.class)
	public B handlerException(InvalidFormatException e) {
		log.error("日期格式不正确,请使用yyyy-MM-dd HH:mm:ss", e);
		return B.create(ErrorCode.PARAM_ERR.reMsg(),"日期格式不正确,请使用yyyy-MM-dd HH:mm:ss");
	}

	@ExceptionHandler(MissingServletRequestParameterException.class)
	public B handlerException(MissingServletRequestParameterException e) {
		log.error("参数异常，请正确填入参数:" + e.getMessage());
		return B.create(ErrorCode.PARAM_ERR.reMsg(),"参数异常，请正确填入参数",e.toString());
	}

	@ExceptionHandler(MethodArgumentNotValidException.class)
	public R handleMethodArgumentNotValidException(MethodArgumentNotValidException e, HttpServletRequest request){

		Method method = e.getParameter().getMethod();
		FieldError fieldError = e.getBindingResult().getFieldError();

		MethodStoErr err = new MethodStoErr();
		err.setDetail( fieldError.getField() + "-" + fieldError.getDefaultMessage() + "-" + fieldError.getCode() );
		err.setErrMethod( method.getDeclaringClass().getName() + "." + method.getName() );
		err.setErrPath( request.getRequestURL().toString() );
		err.setParameterType(e.getParameter().getParameterType().getName());
		err.setParameter(e.getBindingResult().getTarget().toString());
		log.warn( JsonUtil.toJson(err) );
		return R.successd(ErrorCode.PARAM_ERR.reMsg(),err);
	}

	@ExceptionHandler(DuplicateKeyException.class)
	public B handlerDuplicateKeyException(DuplicateKeyException e, HttpServletRequest request){
		B result = B.create(ErrorCode.DUPLICATE_ERR.reMsg());
		result.put("errPath",request.getRequestURL().toString() );
		log.warn( e.getCause().getMessage() );
		DDErrorUtil.directSend(getErrMsg(e,"数据存在重复").get(0));
		return result.put("errMsg",e.getCause().getMessage());
	}

	//获取错误信息
	private List<String> getErrMsg(Exception e,String diyMsg){
		String requestId = MDC.get(FinalStr.requestId);
		LocalDateTime now = LocalDateTime.now();
		List<String> list = new ArrayList<>();
		//返回具体错误信息
		String err = e.toString();

		//获取错行
		StackTraceElement[] stackTrace = e.getStackTrace();
		StackTraceElement fristElement = stackTrace[0];
		if(stackTrace != null && stackTrace.length > 0){
			err = "Fail:" + fristElement.getLineNumber() + "  msg:" + err +
					"  errMsg:" + e.getMessage();

		}
		String ddMsg =
				"-----RequestId-----\r\n" + requestId
				+ "\r\n-----错误信息-----\r\n" + (StringUtils.isNotBlank(diyMsg) ? diyMsg : err)
				+ "\r\n-----具体报错-----\r\n" + JsonUtil.toJson(stackTrace[0])
				+ "\r\n --时间:" + now;
		list.add(err + "  Date:" +  now);
		list.add(ddMsg);
		log.error("具体报错2",e);
		return list;
	}

}